Skip to content

[Woo POS] Products grid #11539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

samiuelson
Copy link
Contributor

@samiuelson samiuelson commented May 17, 2024

Closes: #11516

Description

This PR adds a very basic grid view showing products at the left side of the Cart screen. The product grid supports pagination.

Not included:

  • Filtering simple products
  • Loading indicators
  • Item selection
  • Unit tests

The above will come in the next PRs for the sake of brevity.

Testing instructions

Test if all the products are loading. At the current stage all products should be visible, not only simple products.

Images/gif

grid.mp4
  • I have considered if this change warrants release notes and have added them to RELEASE-NOTES.txt if necessary. Use the "[Internal]" label for non-user-facing changes.

@samiuelson samiuelson linked an issue May 17, 2024 that may be closed by this pull request
@dangermattic
Copy link
Collaborator

dangermattic commented May 17, 2024

3 Warnings
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
⚠️ Class ProductSelectorViewModel is missing tests, but unit-tests-exemption label was set to ignore this.
⚠️ Class ProductsDataSourceImpl is missing tests, but unit-tests-exemption label was set to ignore this.

Generated by 🚫 Danger

@samiuelson samiuelson changed the title 11516 woo pos core home screen UI product grid cart top app bar [Woo POS] Products grid May 17, 2024
@samiuelson samiuelson added this to the 18.8 milestone May 17, 2024
@samiuelson samiuelson requested review from malinajirka and kidinov May 17, 2024 15:52
@samiuelson samiuelson marked this pull request as ready for review May 17, 2024 15:59
@wpmobilebot
Copy link
Collaborator

wpmobilebot commented May 17, 2024

📲 You can test the changes from this Pull Request in WooCommerce Android by scanning the QR code below to install the corresponding build.

App Name WooCommerce Android
FlavorJalapeno
Build TypeDebug
Commitd522c92
Direct Downloadwoocommerce-prototype-build-pr11539-d522c92.apk

class POSModule {
@Provides
fun provideProductList(
handler: ProductListHandler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we should indeed use this as it will be faster than making a new class and so far there's no differences with POS requirements. I think we can swap it out later if the POS functionality requires changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions that come to mind which might be worth answering: How complex will it be to revert this decision later? What are the risks of affecting the store-management section of the app (eg. introducing new bugs, etc)? Does this decision make the store-management section more complex? Does it increase maintainability (eg. more testing is needed, code is more difficult to reason about, ...)? What are reasons for change to the ProductListHandler - is it dependant on the use-case (POS vs store-management) or is it a generic component?

Just to be clear, I'm not challenging whether we should re-use or not, since I honestly don't know for this case. I'm just trying to better understand the implications. In general, I'm all up for re-using where we can, but at the same time I don't think we should re-use just because the code can be invoked from two different sections of the app and works (now) -> the reason for change should imo be the same for all the sections in which the code is used or the code probably shouldn't be re-used.

Copy link
Contributor Author

@samiuelson samiuelson May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for raising these questions, @malinajirka. Let me share my POV.

How complex will it be to revert this decision later?

My reasoning is we won't need to revert this decision. We may want to write a new implementation of the ProductsDataSource interface (I don't see a clear reason for doing it now though):

interface ProductsDataSource {
    val products: Flow<List<Product>>
    suspend fun loadProducts()
    suspend fun loadMore()
}

For now, employing the existing ProductListHandler's code to implement the interface saves us time. I think the important question could be – is the ProductsDataSource designed well for our use case?

What are the risks of affecting the store-management section of the app (eg. introducing new bugs, etc)?

An optimistic assumption is that we won't need to modify ProductListHandler implementation at all, which saves us development effort now. If we have to modify it in the future though, I'd consider forking/copying this class and adjusting it for the POS-specific purpose, or implementing from scratch. That said the store management app will not be affected at all.

Does this decision make the store-management section more complex?

No

Does it increase maintainability (eg. more testing is needed, code is more difficult to reason about, ...)?

Yes, any change to ProductListHandler will be reflected in both POS and store management modes. It will require additional awareness. If we want to avoid this, we can consider forking/copying this class.

What are reasons for change to the ProductListHandler - is it dependant on the use-case (POS vs store-management) or is it a generic component?

My POV is that ProductListHandler is a generic component, mature, and with a narrow scope of responsibilities – a helper class for handling product pagination and search modes.

Let me know what you think, @malinajirka, @kidinov, @backwardstruck.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the important question could be – is the ProductsDataSource designed well for our use case?

To me this is the main question. I personally haven't worked much with ProductListHandler, ProductSelectorRepository and WCProductStore. Is this any good? Idon't recall any complains that products are missing or not loading or something like that, so maybe a good sign. And also there is a good sign it doesn't do 10 network requests when products screen is open in the current app =)

API there is a bit weird thoughloadFromCacheAndFetch and loadMore returns Result<Unit> which we don't use at the moment

Copy link
Contributor Author

@samiuelson samiuelson May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point about unused returns – we don't handle errors in this PR. I think we should add some sort of Result return types to loadProducts() and loadMore() functions and handle errors in the VM (I added a task for it).

Some other considerations related to interface design and DI:

  • Do we want to support swipe to refresh
  • Do we need a possibility to invalidate the data
  • Do we want ProductsDataSource to be a singleton? This works as a naive in-memory data cache (optimizes perfomance), but also prevents from reloading products (risk of data being stale; also a problem in the app currently)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to support swipe to refresh

I guess this is more design question, but I think yes we should

Do we need a possibility to invalidate the data

I'd invalidate that on swipe to refresh gesture after success comes from the backend

Do we want ProductsDataSource to be a singleton? This works as a naive in-memory data cache (optimizes perfomance), but also prevents from reloading products (risk of data being stale; also a problem in the app currently)

Won't see keep what we show in memory in VM anyway?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An optimistic assumption is that we won't need to modify ProductListHandler implementation at all, which saves us development effort now. If we have to modify it in the future though, I'd consider forking/copying this class and adjusting it for the POS-specific purpose, or implementing from scratch. That said the store management app will not be affected at all.

I think one more factor which might play a role here is that we might not be the developers who will need to modify the code in the future. Will the other devs realize, they should create a copy/replace/refactor the component instead of branching POS vs not-POS and therefore coupling these two modes?

Having said all that, I'm personally also inclined towards re-using it, since as you said My POV is that ProductListHandler is a generic component, mature, and with a narrow scope of responsibilities – a helper class for handling product pagination and search modes.. But in general, I think our approach to re-using needs to consider future implications and whether we are setting future-us and other-devs for a success or a failure.

@codecov-commenter
Copy link

codecov-commenter commented May 17, 2024

Codecov Report

Attention: Patch coverage is 0% with 72 lines in your changes are missing coverage. Please review.

Project coverage is 40.39%. Comparing base (2b56352) to head (76056fd).
Report is 71 commits behind head on trunk.

Current head 76056fd differs from pull request most recent head 94ec44a

Please upload reports for the commit 94ec44a to get more accurate results.

Files Patch % Lines
...android/ui/woopos/home/products/ProductSelector.kt 0.00% 42 Missing ⚠️
...i/woopos/home/products/ProductSelectorViewModel.kt 0.00% 15 Missing ⚠️
.../ui/woopos/home/products/ProductsDataSourceImpl.kt 0.00% 8 Missing ⚠️
...merce/android/ui/woopos/home/products/ViewState.kt 0.00% 7 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##              trunk   #11539      +/-   ##
============================================
- Coverage     40.44%   40.39%   -0.05%     
  Complexity     5196     5196              
============================================
  Files          1083     1087       +4     
  Lines         62996    63068      +72     
  Branches       8628     8642      +14     
============================================
  Hits          25479    25479              
- Misses        35224    35296      +72     
  Partials       2293     2293              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@malinajirka malinajirka self-assigned this May 20, 2024
Copy link
Contributor

@malinajirka malinajirka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @samiuelson! I've done a quick pass and left some questions. Please bear in mind that I'm not that familiar with Compose and not at all familiar with Compose navigation, so my comments might not make sense.

Overall, I didn't spot anything which would be blocking the merge. I'm approving it and leaving the merge to you or others.

@Composable
private fun WooPosCartScreen(onButtonClicked: () -> Unit) {
private fun WooPosCartScreen(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I'm wondering if we could build the cart section and the product selector more independently. It seems we are assuming they'll always be on the same screen. Or perhaps the part which makes this a bit confusing for me is that onLoadMore feels irrelevant to the cart section but still is one of the parameters to WooPosCartScreen.

P.S. I realize this is just a skeleton, but 'onButtonClicked' feels like a very generic name. Could we perhaps rename it to something more specific to make the code easier to reason about?

Copy link
Contributor

@kidinov kidinov May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also reconsider packaging here and naming. Maybe call the screen with scaffold CartBuildScreen (and package as well) and inside of it can have 2 packages: cart and productselector?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I split the cart and product selector into separate packages (4c7d9c8). They are now parts of the Home screen and have separate VMs.

class POSModule {
@Provides
fun provideProductList(
handler: ProductListHandler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions that come to mind which might be worth answering: How complex will it be to revert this decision later? What are the risks of affecting the store-management section of the app (eg. introducing new bugs, etc)? Does this decision make the store-management section more complex? Does it increase maintainability (eg. more testing is needed, code is more difficult to reason about, ...)? What are reasons for change to the ProductListHandler - is it dependant on the use-case (POS vs store-management) or is it a generic component?

Just to be clear, I'm not challenging whether we should re-use or not, since I honestly don't know for this case. I'm just trying to better understand the implications. In general, I'm all up for re-using where we can, but at the same time I don't think we should re-use just because the code can be invoked from two different sections of the app and works (now) -> the reason for change should imo be the same for all the sections in which the code is used or the code probably shouldn't be re-used.

import com.woocommerce.android.model.Product
import kotlinx.coroutines.flow.Flow

interface ProductsDataSource {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I frankly don't see why we need an interface in this case. ProductsDataSourceImpl defines an interface with it's public methods anyway

Copy link
Contributor

@kidinov kidinov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I left a few suggestion, please take a look

@backwardstruck backwardstruck modified the milestones: 18.8, 18.9 May 24, 2024
@samiuelson samiuelson marked this pull request as draft May 30, 2024 07:45
@samiuelson
Copy link
Contributor Author

I'm converting it to draft PR because we want to focus first on implementing the whole navigation flow rather than individual screens. This PR can be reused or serve as inspiration for implementing the product selector pane though.

@hichamboushaba hichamboushaba modified the milestones: 18.9, 19.0 May 31, 2024
@malinajirka
Copy link
Contributor

we want to focus first on implementing the whole navigation flow rather than individual screens.

@samiuelson Could you please share an additional context about this direction - a link to a related conversation is fine. Thanks!

@samiuelson
Copy link
Contributor Author

samiuelson commented Jun 3, 2024

@samiuelson Could you please share an additional context about this direction - a link to a related conversation is fine. Thanks!

@malinajirka, p1717001202981269/1716809008.635649-slack-C070SJRA8DP During the POS sync meeting we analyzed how the UX concepts are evolving (i1 vs i2) and realized that until the UX flow is final we won't know exactly how to share state between screens/panes. That's why we asked to speed up the final decision on the UX last week. I'm resuming the work on this PR as we have clarity on the navigation.

@samiuelson samiuelson changed the base branch from trunk to 11644-woo-pos-implement-skeleton-of-the-i3-of-the-wireframes June 3, 2024 20:11
Base automatically changed from 11644-woo-pos-implement-skeleton-of-the-i3-of-the-wireframes to trunk June 3, 2024 21:15
@samiuelson samiuelson changed the base branch from trunk to 11644-woo-pos-implement-skeleton-of-the-i3-of-the-wireframes-2 June 4, 2024 08:45
@samiuelson samiuelson requested review from kidinov and malinajirka June 4, 2024 08:56
@samiuelson samiuelson marked this pull request as ready for review June 4, 2024 08:56
@samiuelson
Copy link
Contributor Author

👋🏼 @malinajirka, @kidinov I've addressed your comments, and updated the branch with the 11644-woo-pos-implement-skeleton-of-the-i3-of-the-wireframes-2 branch containing the static bottom toolbar. Could you give it another look?

Screen_recording_20240604_104350.mp4

In the next iteration, I will focus on syncing item selection state between Cart and Product Selector and creating the Order.


internal const val HOME_ROUTE = "home"

internal fun NavGraphBuilder.homeScreen(onCheckoutClick: () -> Unit) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed "Cart" to "Home" as the cart is only one of the panes on the home screen.

Copy link
Contributor

@malinajirka malinajirka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samiuelson Thank you for working on this. I've reviewed the PR and re-tested it locally - I think it's a great prototype we can iterate on. I'd merge it, but it seems the CI is failing - feel free to merge it after that's fixed.

@samiuelson samiuelson changed the base branch from 11644-woo-pos-implement-skeleton-of-the-i3-of-the-wireframes-2 to trunk June 4, 2024 09:52
@samiuelson samiuelson enabled auto-merge June 4, 2024 10:22
@samiuelson samiuelson merged commit b94f53e into trunk Jun 4, 2024
13 of 14 checks passed
@samiuelson samiuelson deleted the 11516-woo-pos-core-home-screen-ui--product-grid-cart-top-app-bar branch June 4, 2024 10:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Woo POS] Home screen – basic UI and products grid
8 participants